#!/usr/bin/env python3
import os

import common
from shell_helpers import LF

class Main(common.BuildCliFunction):
    def __init__(self):
        super().__init__(
            defaults={
                'gcc_which':'crosstool-ng',
            },
            description='''\
Build the baremetal examples with crosstool-NG.
''',
            supported_archs=common.consts['crosstool_ng_supported_archs']
        )

    def build(self):
        build_dir = self.get_build_dir()
        bootloader_obj = os.path.join(
            self.env['baremetal_build_lib_dir'],
            'bootloader{}'.format(self.env['obj_ext'])
        )
        common_obj = os.path.join(
            self.env['baremetal_build_lib_dir'],
            self.env['common_basename_noext'] + self.env['obj_ext']
        )
        syscalls_basename_noext = 'syscalls'
        syscalls_src = os.path.join(
            self.env['baremetal_source_lib_dir'],
            syscalls_basename_noext + self.env['c_ext']
        )
        syscalls_obj = os.path.join(
            self.env['baremetal_build_lib_dir'],
            syscalls_basename_noext + self.env['obj_ext']
        )
        common_objs = [common_obj, syscalls_obj]
        cflags = [
            '-I', self.env['baremetal_source_lib_dir'], LF,
            '-I', self.env['root_dir'], LF,
            '-O0', LF,
            '-ggdb3', LF,
            '-mcpu={}'.format(self.env['mcpu']), LF,
            '-nostartfiles', LF,
        ]
        cflags_after = ['-lm']
        if self.env['emulator'] == 'gem5':
            if self.env['machine'] == 'VExpress_GEM5_V1':
                entry_address = 0x80000000
                uart_address = 0x1c090000
            elif self.env['machine'] == 'RealViewPBX':
                entry_address = 0x10000
                uart_address = 0x10009000
            else:
                raise Exception('unknown machine: ' + self.env['machine'])
            cflags.extend(['-D', 'GEM5'.format(uart_address), LF])
        else:
            entry_address = 0x40000000
            uart_address = 0x09000000
        os.makedirs(build_dir, exist_ok=True)
        os.makedirs(self.env['baremetal_build_lib_dir'], exist_ok=True)
        src = os.path.join(
            self.env['baremetal_source_lib_dir'],
            '{}{}'.format(
                self.env['arch'],
                self.env['asm_ext']
            )
        )
        if self.need_rebuild([src], bootloader_obj):
            self.sh.run_cmd(
                [self.env['gcc'],  LF] +
                cflags +
                [
                    '-c', LF,
                    '-o', bootloader_obj, LF,
                    src, LF,
                ] +
                cflags_after
            )
        for src, obj in [
            (self.env['common_c'], common_obj),
            (syscalls_src, syscalls_obj),
        ]:
            if self.need_rebuild([src, self.env['common_h']], obj):
                self.sh.run_cmd(
                    [self.env['gcc'],  LF] +
                    cflags +
                    [
                        '-c', LF,
                        '-D', 'UART0_ADDR={:#x}'.format(uart_address), LF,
                        '-o', obj, LF,
                        src, LF,
                    ] +
                    cflags_after
                )
        for subpath in [
            '',
            'interactive',
            self.env['baremetal_source_arch_subpath'],
            os.path.join(self.env['baremetal_source_arch_subpath'], 'no_bootloader'),
        ]:
            in_dir = os.path.join(self.env['baremetal_source_dir'], subpath)
            if os.path.isdir(in_dir):
                out_dir = os.path.join(self.env['baremetal_build_dir'], subpath)
                os.makedirs(out_dir, exist_ok=True)
                common_objs_bootloader = common_objs.copy()
                if os.path.basename(subpath) != 'no_bootloader':
                    common_objs_bootloader.append(bootloader_obj)
                for in_basename in sorted(os.listdir(in_dir)):
                    in_path = os.path.join(in_dir, in_basename)
                    in_name, in_ext = os.path.splitext(in_basename)
                    if  (
                        os.path.isfile(in_path) and
                        in_ext in (self.env['c_ext'], self.env['asm_ext'])
                    ):
                        main_obj = os.path.join(
                            out_dir,
                            '{}{}'.format(
                                in_name,
                                self.env['obj_ext']
                            )
                        )
                        src = os.path.join(self.env['baremetal_source_dir'], in_path)
                        if self.need_rebuild([src, self.env['common_h']], main_obj):
                            self.sh.run_cmd(
                                [self.env['gcc'],  LF] +
                                cflags +
                                [
                                    '-c', LF,
                                    '-o', main_obj, LF,
                                    src, LF,
                                ] +
                                cflags_after
                            )
                        objs = common_objs_bootloader + [main_obj]
                        out = os.path.join(out_dir, in_name + self.env['baremetal_build_ext'])
                        if self.need_rebuild(objs + [self.env['baremetal_link_script']], out):
                            self.sh.run_cmd(
                                [self.env['gcc'],  LF] +
                                cflags +
                                [
                                    '-Wl,--section-start=.text={:#x}'.format(entry_address), LF,
                                    '-o', out, LF,
                                    '-T', self.env['baremetal_link_script'], LF,
                                ] +
                                self.sh.add_newlines(objs) +
                                cflags_after
                            )


    def get_build_dir(self):
        return self.env['baremetal_build_dir']

if __name__ == '__main__':
    Main().cli()
